package com.suray.wcs.service.core.path.api.impl;

import com.suray.basic.wcs.path.core.Node;
import com.suray.basic.wcs.utils.LoggerUtil;
import com.suray.basic.wcs.utils.ModuleType;
import com.suray.basic.wcs.utils.ParamConfig;
import com.suray.commin.core.utils.SpringContextHolder;
import com.suray.system.run.util.PortFuncUtil;
import com.suray.system.run.util.PortTaskContainer;
import com.suray.wcs.res.enums.RgvReTaskType;
import com.suray.wcs.service.enums.TaskRgvProgress;
import com.suray.wcs.service.enums.TaskType;
import com.suray.wcs.service.init.SystemInit;
import com.suray.wcs.service.pojo.Rgv;
import com.suray.wcs.service.system.bo.ConnectNodeBO;
import com.suray.wcs.service.system.entity.NodeDB;
import com.suray.wcs.service.system.entity.PortDB;
import com.suray.wcs.service.system.entity.PortTaskConfigDB;
import com.suray.wcs.service.system.entity.TaskDB;
import com.suray.wcs.service.system.service.impl.*;
import com.suray.wcs.service.test.base.BaseThread;
import com.suray.wcs.service.util.WcsUtil;

import java.util.Collections;
import java.util.List;

/**
 * 库口重新选择
 */
public class TaskPortReAssignImpl extends BaseThread {

    private TaskDBServiceImpl taskDBService = SpringContextHolder.getBean(TaskDBServiceImpl.class);
    private PortDBServiceImpl portDBService = SpringContextHolder.getBean(PortDBServiceImpl.class);
    private NodeDBServiceImpl nodeDBService = SpringContextHolder.getBean(NodeDBServiceImpl.class);
    private ConnectConfServiceImpl connectConfService = SpringContextHolder.getBean(ConnectConfServiceImpl.class);
    private PortTaskConfigDBServiceImpl portTaskConfigDBService
            = SpringContextHolder.getBean(PortTaskConfigDBServiceImpl.class);

    public TaskPortReAssignImpl() {
        super("重分配出库口", 1000, ModuleType.WCS_SYSTEM);
    }

    @Override
    protected void process() {
        List<TaskDB> taskDBList = taskDBService.findAllUnFinishTask(TaskType.OUT);
        List<PortTaskConfigDB> configDBS = portTaskConfigDBService.list();

        for (TaskDB taskDB : taskDBList) {
            if (taskDB.getOutPortsStr() == null) {
                continue;
            }

            if (taskDB.getTaskPortId() == null) {
                continue;
            }

            if (taskDBService.findTaskByPreTaskId(taskDB.getTaskId()) != null) {
                //前置任务，不重新分配库口
                continue;
            }

            PortDB portDB = portDBService.selectByPortCode(taskDB.getTaskPortId());
            PortTaskContainer container = PortFuncUtil.analyzeOutPortTaskNum(taskDBList);
            List<PortContainer> portContainers = PortFuncUtil.analyzePortContainer(taskDB, container, true);

            analyzePortsTaskNum(portContainers, portDB, configDBS, taskDB);

            if (portDB != null && !PortFuncUtil.isPortEnable(portDB)) {
                //当前出库口被禁用
                LoggerUtil.info(portDB.getPortName() + "被禁用，开始重新分配库口:" + taskDB, this.getClass());
                reAssignOutPort(taskDB, portContainers, configDBS);
            }
        }
    }

    /**
     * 根据库口数量确认是否要更改库口
     * @param portContainers
     * @param portDB
     * @param configDBS
     * @param taskDB
     */
    private void analyzePortsTaskNum(List<PortContainer> portContainers, PortDB portDB,
                                     List<PortTaskConfigDB> configDBS, TaskDB taskDB) {
        if (ParamConfig.portTaskNumGap < 2) {
            return;
        }

        if (taskDB.getRgvProgress() == TaskRgvProgress.RGV_TO_END) {
            //如果小车已取货不进行库口更改
            return;
        }

        PortContainer currentPort = null;
        for (PortContainer portContainer : portContainers) {
            if (portDB.getPortId().equals(portContainer.getPortDB().getPortId())) {
                currentPort = portContainer;
                break;
            }
        }

        if (currentPort == null) {
            //当前库口被禁用
            return;
        }

        for (PortContainer portContainer : portContainers) {
            if (portContainer.equals(currentPort)) {
                continue;
            }

            if (!PortFuncUtil.checkPortTaskConfig(portContainer, configDBS, taskDB.selectStartNode().getZ())) {
                continue;
            }

            if ((portContainer.getTaskNum() + ParamConfig.portTaskNumGap) < currentPort.getTaskNum()) {
                if (changeOutPort(taskDB, portContainer.getPortDB())) {
                    break;
                }
            }
        }
    }

    /**
     * 重新选择出库口
     * 接驳任务，前置任务不支持重新选择
     * 后置任务可以重新换接口
     * @param taskDB
     * @param portContainers
     * @param configDBS
     */
    private void reAssignOutPort(TaskDB taskDB, List<PortContainer> portContainers, List<PortTaskConfigDB> configDBS) {
        for (PortContainer portContainer : portContainers) {
            if (!PortFuncUtil.checkPortTaskConfig(portContainer, configDBS, taskDB.selectStartNode().getZ())) {
                continue;
            }

            PortDB portDB = portContainer.getPortDB();
            if (changeOutPort(taskDB, portDB)) {
                break;
            }
        }
    }

    private boolean changeOutPort(TaskDB taskDB, PortDB portDB) {
        NodeDB endNodeDB = null;
        try {
            endNodeDB = WcsUtil.getRgvEndNode(taskDB.selectStartNode(), portDB, taskDB.getTaskType(),
                    nodeDBService);
        } catch (Exception e) {
            LoggerUtil.warn("未找到合适的终点，库口：" + portDB.getPortName() + ", 任务：" + taskDB.getTaskId(),
                    this.getClass());
        }
        ConnectNodeBO endPlanBO = null;
        if (endNodeDB == null) {
            NodeDB startNodeDB = new NodeDB();
            Node startNode = taskDB.selectStartNode();
            startNodeDB.setGridx(startNode.getX());
            startNodeDB.setGridy(startNode.getY());
            startNodeDB.setGridz(startNode.getZ());
            endPlanBO = WcsUtil.getConnectNodeInOrOut(null, portDB.getPortCode(),
                    startNodeDB, null, TaskType.OUT.getTypeNum(), nodeDBService, connectConfService);

            if (endPlanBO != null) {
                endNodeDB = endPlanBO.getNodedb();
            }
        }
        if (endNodeDB != null) {
            Node endNode;
            if (endPlanBO == null) {
                endNode = new Node(endNodeDB.getGridx(), endNodeDB.getGridy(), endNodeDB.getGridz());
                taskDB.setTaskPortId(portDB.getPortCode());
            } else {
                endNode = generalAftTask(taskDB, endPlanBO);
            }

            if (endNode != null) {
                taskDBService.improveTask(taskDB, endNode);
                Integer rgvNo = taskDB.getProcessingRgvNo();
                if (rgvNo != null) {
                    Rgv rgv = SystemInit.getByRgvNo(rgvNo);
                    if (rgv.getCurTask() != null && rgv.getCurTask().getTaskId().equals(taskDB.getTaskId())) {
                        //小车执行当前任务时才能进行任务打断
                        rgv.changeReTaskType(RgvReTaskType.CHANGE_OUT_PORT);
                    }
                }

                return true;
            }
        }

        return false;
    }

    /**
     * 生成后置任务
     * @param taskDB
     * @param endPlanBO
     * @return
     */
    private Node generalAftTask(TaskDB taskDB, ConnectNodeBO endPlanBO) {
        if (endPlanBO == null || endPlanBO.getPreConnect() == null || endPlanBO.getSufNodeConnect() == null) {
            //无后置任务
            return null;
        }

        TaskDB aftTask = new TaskDB();
        aftTask.setStartNode(taskDB.selectStartNode());
        aftTask.setEndNode(endPlanBO.getPreConnect());
        aftTask.setTaskType(taskDB.getTaskType());
        aftTask.setTaskPortId(taskDB.getConnectPortCode());
        aftTask.setPriorityCode(taskDB.getPriorityCode());
        aftTask.setBarCode(taskDB.getBarCode());
        aftTask.setWmsTaskId(null);
        aftTask.setTaskPortId(taskDB.getTaskPortId());
        aftTask.setPreTaskId(taskDB.getTaskId());

        taskDBService.insertTask(Collections.singletonList(aftTask));

        taskDB.setTaskPortId(endPlanBO.getConnectPortCode());

        return endPlanBO.getPreConnect();
    }
}
